package pay

import (
	"bytes"
	"crypto/md5"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"net/http"
	"sort"
	"strconv"
	"strings"
	"talent.com/agency_im/conf"
	"talent.com/server/durotar.git/log"
	"talent.com/server/durotar.git/tools/tz"
)

type resp struct {
	Status int             `json:"status"`
	Msg    string          `json:"msg"`
	Data   json.RawMessage `json:"data"`
}

var client = &http.Client{}

// 查询代理所有可用的支付分类
func getAgencyPayTypes(agencyID uint64, mchID uint64, playerID uint64) (*[]PayType, error) {

	url := conf.ServerConfig().Pay.Host + "im/agency/pay_types/"

	params := map[string]string{
		"agency_id": strconv.Itoa(int(agencyID)),
		"mch_id":    strconv.Itoa(int(mchID)),
		"user_id":   strconv.Itoa(int(playerID)),
		"timestamp": strconv.Itoa(int(tz.GetNowTs())),
	}

	result := new([]PayType)
	if err := doGet(url, params, result); err != nil {
		log.Error("get agency pay types error, agency id : %v, user id: %v, cause: %v", agencyID, playerID, err)
		return nil, err
	}
	return result, nil
}

// 根据支付方式，在代理所有该支付方式下的收款账户中，匹配一个最优的
func getAgencyReceiptAddressByPayType(agencyID uint64, mchID uint64, userID uint64, payType PayType, byAgency bool) (*ReceiptAddress, error) {

	url := conf.ServerConfig().Pay.Host + "im/agency/get_receipt_address_by_pay_type/"

	params := map[string]string{
		"agency_id": strconv.Itoa(int(agencyID)),
		"mch_id":    strconv.Itoa(int(mchID)),
		"user_id":   strconv.Itoa(int(userID)),
		"pay_type":  strconv.Itoa(int(payType)),
		"timestamp": strconv.Itoa(int(tz.GetNowTs())),
	}

	if byAgency {
		params["by_agency"] = "1"
	} else {
		params["by_agency"] = "0"
	}

	var result ReceiptAddress
	if err := doGet(url, params, &result); err != nil {
		log.Error("get agency receipt address error, agency id : %v, user id: %v, cause: %v", agencyID, userID, err)
		return nil, err
	}
	return &result, nil
}

// 创建代理充值订单
func createPayOrder(agencyID uint64, mchID uint64, userID uint64, amount int64, ip string, receiptAddressID int, payType PayType) (*RechargeOrder, error) {
	url := conf.ServerConfig().Pay.Host + "im/agency/recharge_orders/"

	params := map[string]string{
		"agency_id": strconv.Itoa(int(agencyID)),
		"mch_id":    strconv.Itoa(int(mchID)),
		"user_id":   strconv.Itoa(int(userID)),
		"amount":    fmt.Sprintf("%v", amount),
		//"ip":          ip,
		"pay_type":           strconv.Itoa(int(payType)),
		"receipt_address_id": strconv.Itoa(receiptAddressID),
		"timestamp":          strconv.Itoa(int(tz.GetNowTs())),
	}

	var payOrder RechargeOrder
	if err := doPost(url, params, &payOrder); err != nil {
		log.Error("create pay order fail : agencyID: [%v], mchID: [%v], userID: [%v], cause: %v", agencyID, mchID, userID, err)
		return nil, err
	}
	return &payOrder, nil
}

// 获取代理人员信息
func getAgencyInfo(agencyID uint64) (*AgencyInfo, error) {
	url := conf.ServerConfig().Pay.Host + "im/agency/get_agency_info/"

	params := map[string]string{
		"agency_id": strconv.Itoa(int(agencyID)),
		"timestamp": strconv.Itoa(int(tz.GetNowTs())),
	}

	var result AgencyInfo
	if err := doGet(url, params, &result); err != nil {
		log.Error("get agency info error, agency id: %v, cause: %v", agencyID, err)
		return nil, err
	}
	return &result, nil
}

// 让代理下班
func makeAgentOffWork(agencyID uint64) (bool, error) {
	url := conf.ServerConfig().Pay.Host + "im/agency/off_work/"
	params := map[string]string{
		"agency_id": strconv.Itoa(int(agencyID)),
		"timestamp": strconv.Itoa(int(tz.GetNowTs())),
	}

	if err := doPost(url, params, nil); err != nil {
		log.Error("make agency off work error,  agency_id: %v, cause: %v", agencyID, err)
		return false, err
	}
	return true, nil
}

// 获取代理快捷回复
func getAgencyQuickReply(agencyID uint64) (*[]QuickReply, error) {
	url := conf.ServerConfig().Pay.Host + "im/agency/quick_reply/"
	params := map[string]string{
		"agency_id": strconv.Itoa(int(agencyID)),
		"timestamp": strconv.Itoa(int(tz.GetNowTs())),
	}

	result := new([]QuickReply)
	if err := doGet(url, params, &result); err != nil {
		log.Error("getAgencyQuickReply error,  agency_id: %v, cause: %v", agencyID, err)
		return result, err
	}
	return result, nil
}

// 投诉代理
func complainAgency(UserID uint64, MchID uint64, agencyID uint64, complaintType uint64, images []string, complaintContent string) (*ComplainResp, error) {
	url := conf.ServerConfig().Pay.Host + "im/agency/complaint/"
	params := map[string]string{
		"agency_id":      strconv.Itoa(int(agencyID)),
		"user_id":        strconv.Itoa(int(UserID)),
		"mch_id":         strconv.Itoa(int(MchID)),
		"timestamp":      strconv.Itoa(int(tz.GetNowTs())),
		"complaint_types": strconv.Itoa(int(complaintType)),
	}
	if complaintContent != "" {
		params["complaint_content"] = complaintContent
	}

	if images != nil {
		params["images"] = strings.Join(images, "|")
	}
	var result ComplainResp
	if err := doPost(url, params, &result); err != nil {
		log.Error("complainAgency error,  agency_id: %v, cause: %v", agencyID, err)
		return nil, err
	}
	return &result, nil
}

// 获取玩家用户数据
func getPlayerInfo(mchID uint64, userID uint64, agencyID uint64) (*PlayerInfo, error) {
	url := conf.ServerConfig().Pay.Host + "im/agency/get_player_info/"

	params := map[string]string{
		"agency_id": strconv.Itoa(int(agencyID)),
		"mch_id":    strconv.Itoa(int(mchID)),
		"user_id":   strconv.Itoa(int(userID)),
		"timestamp": strconv.Itoa(int(tz.GetNowTs())),
	}

	var result PlayerInfo
	if err := doGet(url, params, &result); err != nil {
		log.Error("get player info error, mch id : %v, agency: %v, user id: %v, cause: %v", mchID, agencyID, userID, err)
		return nil, err
	}
	return &result, nil
}

// 拉黑
func addBlacklist(agencyID uint64, mchID uint64, userID uint64, remark string) error {
	url := conf.ServerConfig().Pay.Host + "im/agency/blacklist/"
	params := map[string]string{
		"agency_id": strconv.Itoa(int(agencyID)),
		"mch_id":    strconv.Itoa(int(mchID)),
		"user_id":   strconv.Itoa(int(userID)),
		"remark":    remark,
		"timestamp": strconv.Itoa(int(tz.GetNowTs())),
	}

	if err := doPost(url, params, nil); err != nil {
		log.Error("add blacklist,  agency_id: %v, mch: %v, user: %v cause: %v", agencyID, mchID, userID, err)
		return err
	}
	return nil
}

// 检查是否是黑名单
func isBlacklist(agencyID uint64, mchID uint64, userID uint64) (bool, error) {
	url := conf.ServerConfig().Pay.Host + "im/agency/blacklist/check/"
	params := map[string]string{
		"agency_id": strconv.Itoa(int(agencyID)),
		"mch_id":    strconv.Itoa(int(mchID)),
		"user_id":   strconv.Itoa(int(userID)),
		"timestamp": strconv.Itoa(int(tz.GetNowTs())),
	}

	var result CheckBlacklist
	if err := doGet(url, params, &result); err != nil {
		log.Error("check blacklist,  agency_id: %v, mch: %v, user: %v cause: %v", agencyID, mchID, userID, err)
		return false, err
	}
	return result.IsBlacklist, nil
}

func doGet(url string, params map[string]string, respData interface{}) error {
	req, err := http.NewRequest("GET", url, nil)
	if err != nil {
		return err
	}

	q := req.URL.Query()
	for k, v := range params {
		q.Add(k, v)
	}
	sign := signParameters(params)
	q.Add("sign", sign)
	req.URL.RawQuery = q.Encode()

	return do(req, respData)
}

func doPost(url string, params map[string]string, respData interface{}) error {
	sign := signParameters(params)
	params["sign"] = sign
	paramsBytes, err := json.Marshal(params)
	if err != nil {
		return err
	}
	req, err := http.NewRequest("POST", url, bytes.NewBuffer(paramsBytes))
	if err != nil {
		return err
	}
	req.Header.Set("Content-Type", "application/json")

	return do(req, respData)
}

func do(req *http.Request, respData interface{}) error {
	resp, err := client.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()
	//if respStr, err := ioutil.ReadAll(resp.Body); err != nil {
	//	return err
	//} else {
	//	log.Info("rpc request url: %v, resp: %+v", req.URL, string(respStr))
	//}
	err = decodeHttpResponse(resp.Body, respData)
	if err != nil {
		return err
	}
	return nil
}

func signParameters(params map[string]string) string {
	var keys []string
	for k := range params {
		keys = append(keys, k)
	}

	str := ""
	sort.Strings(keys)
	for _, k := range keys {
		str += fmt.Sprintf("%v=%v&", k, params[k])
	}
	str += fmt.Sprintf("key=%v", conf.ServerConfig().Pay.ApiKey)
	return fmt.Sprintf("%x", md5.Sum([]byte(str)))
}

func decodeHttpResponse(reader io.Reader, data interface{}) error {
	var r = resp{}
	if err := json.NewDecoder(reader).Decode(&r); err != nil {
		return err
	}

	if r.Status != 0 {
		return errors.New(fmt.Sprintf("%v", r.Status))
	}

	json.Unmarshal(r.Data, data)
	return nil
}
